/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */
package cz.muni.fi.pa165.bookingmanager.web;

import cz.muni.fi.pa165.bookingmanager.adapters.*;
import cz.muni.fi.pa165.bookingmanager.dto.CustomerDTO;
import cz.muni.fi.pa165.bookingmanager.dto.HotelDTO;
import cz.muni.fi.pa165.bookingmanager.dto.ReservationDTO;
import cz.muni.fi.pa165.bookingmanager.dto.RoomDTO;
import cz.muni.fi.pa165.bookingmanager.exceptions.IllegalOrphanException;
import cz.muni.fi.pa165.bookingmanager.exceptions.PreexistingEntityException;
import cz.muni.fi.pa165.bookingmanager.other.PaymentStatusEnum;
import java.math.BigDecimal;
import java.sql.Date;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import javax.naming.Context;
import javax.naming.InitialContext;
import javax.naming.NamingException;
import net.sourceforge.stripes.action.*;
import net.sourceforge.stripes.controller.LifecycleStage;
import net.sourceforge.stripes.validation.*;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
 *
 * @author Jiří Holuša
 */
@UrlBinding("/frontend/{$event}")
public class FrontEndActionBean extends MyActionBean {

    final static Logger log = LoggerFactory.getLogger(FrontEndActionBean.class);
    private HotelAdapter hotelAdapter;
    private CustomerAdapter customerAdapter;
    private ReservationAdapter reservationAdapter;
    private RoomAdapter roomAdapter;
    private BookingAdapter bookingAdapter;
    private HotelDTO hotel;
    private RoomDTO room;
    private List<HotelDTO> listHotel = new ArrayList<HotelDTO>();
    private List<RoomDTO> listRoom = new ArrayList<RoomDTO>();
    private List<CustomerDTO> customers = new ArrayList<CustomerDTO>();

    public FrontEndActionBean() throws NamingException {
        Context ctx = new InitialContext();
        hotelAdapter = (HotelAdapter) ctx.lookup("java:global/cz.muni.fi.pa165.bookingmanager_booking_manager-web_war_1.0-SNAPSHOT/HotelAdapterImpl!cz.muni.fi.pa165.bookingmanager.adapters.HotelAdapter");
        customerAdapter = (CustomerAdapter) ctx.lookup("java:global/cz.muni.fi.pa165.bookingmanager_booking_manager-web_war_1.0-SNAPSHOT/CustomerAdapterImpl!cz.muni.fi.pa165.bookingmanager.adapters.CustomerAdapter");
        reservationAdapter = (ReservationAdapter) ctx.lookup("java:global/cz.muni.fi.pa165.bookingmanager_booking_manager-web_war_1.0-SNAPSHOT/ReservationAdapterImpl!cz.muni.fi.pa165.bookingmanager.adapters.ReservationAdapter");
        roomAdapter = (RoomAdapter) ctx.lookup("java:global/cz.muni.fi.pa165.bookingmanager_booking_manager-web_war_1.0-SNAPSHOT/RoomAdapterImpl!cz.muni.fi.pa165.bookingmanager.adapters.RoomAdapter");
        bookingAdapter = (BookingAdapter) ctx.lookup("java:global/cz.muni.fi.pa165.bookingmanager_booking_manager-web_war_1.0-SNAPSHOT/BookingAdapterImpl!cz.muni.fi.pa165.bookingmanager.adapters.BookingAdapter");
    }

    @Before(stages = LifecycleStage.BindingAndValidation, on = {"hotelDetail", "hotelFreeRooms", "bookRoom", "executeBooking", "advancedSearching"})
    public void loadHotelFromDatabase() {
        String ids = getContext().getRequest().getParameter("hotel.id");
        if (ids == null) {
            return;
        }
        this.setHotel(hotelAdapter.getHotelById(Long.parseLong(ids)));
    }

    @Before(stages = LifecycleStage.BindingAndValidation, on = {"bookRoom", "executeBooking"})
    public void loadRoomFromDatabase() {
        String ids = getContext().getRequest().getParameter("room.id");
        if (ids == null) {
            return;
        }
        this.setRoom(roomAdapter.getRoomById(Long.parseLong(ids)));
    }

    @DefaultHandler
    public Resolution home() {
        return new ForwardResolution("/frontend/index.jsp");
    }

    public Resolution hotelsList() {
        log.debug("frontendHotelsList");
        listHotel = hotelAdapter.findAllHotels();
        return new ForwardResolution("/frontend/hotelsList.jsp");
    }

    public Resolution hotelDetail() {
        log.debug("frontendHotelDetail");
        System.out.println(hotel);
        return new ForwardResolution("/frontend/hotelDetail.jsp");
    }

    public HotelDTO getHotelById(Long hotelID) {
        return hotelAdapter.getHotelById(hotelID);
    }

    @HandlesEvent("hotelFreeRooms")
    public Resolution hotelFreeRooms() {
        String day = getContext().getRequest().getParameter("day");
        String month = getContext().getRequest().getParameter("month");
        String year = getContext().getRequest().getParameter("year");
        if (month.length() == 1) {
            month = "0" + month;
        }
        if (day.length() == 1) {
            day = "0" + day;
        }
        String dateToFormat = year + "-" + month + "-" + day;

        Date startDate = Date.valueOf(dateToFormat);

        int duration = Integer.valueOf(getContext().getRequest().getParameter("duration"));
        listRoom = bookingAdapter.findAllFreeRooms(hotel, startDate, duration);

        return new ForwardResolution("/frontend/roomsList.jsp").addParameter("duration", duration).addParameter("startDate", day + ". " + month + ". " + year).addParameter("startDateRaw", dateToFormat);
    }

    public Resolution bookRoom() {
        String startDateRaw = getContext().getRequest().getParameter("startDateRaw");
        String duration = getContext().getRequest().getParameter("duration");

        return new ForwardResolution("/frontend/bookRoom.jsp").addParameter("startDateRaw", startDateRaw).addParameter("duration", duration);
    }

    public Resolution executeBooking() {
        if (customers.isEmpty()) {
            ValidationErrors errors = getContext().getValidationErrors();
            errors.addGlobalError(new LocalizableError("frontend.emptyCustomers"));
            getContext().setValidationErrors(errors);
            return getContext().getSourcePageResolution();
        }

        List<CustomerDTO> tmpCustomers = new ArrayList<CustomerDTO>();
        tmpCustomers.addAll(customers);

        for (CustomerDTO customer : tmpCustomers) {
            try {
                customerAdapter.createCustomer(customer);
            } catch (IllegalOrphanException ex) {
                log.error(ex.toString());
                return new RedirectResolution("/frontend/info.jsp").addParameter("message_key", "frontend.executeBooking.message_ko");
            } catch (PreexistingEntityException ex) {
                log.error(ex.toString());
                return new RedirectResolution("/frontend/info.jsp").addParameter("message_key", "frontend.executeBooking.message_ko");
            }
        }

        int duration = Integer.parseInt(getContext().getRequest().getParameter("duration"));

        ReservationDTO reservation = new ReservationDTO();
        reservation.setRoom(room);
        reservation.setHotel(hotel);
        reservation.setPaid(PaymentStatusEnum.NOT_PAID);
        reservation.setStartDate(Date.valueOf(getContext().getRequest().getParameter("startDateRaw")));
        reservation.setDuration(duration);
        reservation.setTotalPrice((room.getPrice().multiply(BigDecimal.valueOf(duration))));
        reservation.setCustomers(customers);

        try {
            reservationAdapter.createReservation(reservation);
        } catch (IllegalOrphanException ex) {
            log.error(ex.toString());
        } catch (PreexistingEntityException ex) {
            log.error(ex.toString());
        }

        return new RedirectResolution("/frontend/info.jsp").addParameter("message_key", "frontend.executeBooking.message_ok");
    }

    public Resolution quickSearch() {
        String city = getContext().getRequest().getParameter("quick_search_city");
        BigDecimal maxPrice = BigDecimal.valueOf(Integer.parseInt(getContext().getRequest().getParameter("quick_search_maxPrice")));
        int minCapacity = Integer.parseInt(getContext().getRequest().getParameter("quick_search_minCapacity"));

        listHotel = bookingAdapter.searchHotelByConditions(city, maxPrice, minCapacity);

        return new ForwardResolution("/frontend/hotelsList.jsp");
    }

    public Resolution advancedSearch() {
        listHotel = hotelAdapter.findAllHotels();

        return new ForwardResolution("/frontend/advancedSearch.jsp");
    }

    public Resolution advancedSearching() {
        String day = getContext().getRequest().getParameter("day");
        String month = getContext().getRequest().getParameter("month");
        String year = getContext().getRequest().getParameter("year");

        if (month.length() == 1) {
            month = "0" + month;
        }
        if (day.length() == 1) {
            day = "0" + day;
        }
        String dateToFormat = year + "-" + month + "-" + day;

        Date startDate = Date.valueOf(dateToFormat);

        int duration = Integer.valueOf(getContext().getRequest().getParameter("duration"));
        int minCapacity = Integer.valueOf(getContext().getRequest().getParameter("minCapacity"));
        BigDecimal maxPrice = BigDecimal.valueOf(Integer.valueOf(getContext().getRequest().getParameter("maxPrice")));


        List<RoomDTO> freeRoomsList = bookingAdapter.findAllFreeRooms(hotel, startDate, duration);
        List<RoomDTO> roomsInHotel = bookingAdapter.searchRoomInHotel(hotel, minCapacity, maxPrice);

        List<RoomDTO> resultRooms = new ArrayList<RoomDTO>();
        for (RoomDTO tmpRoom : roomsInHotel) {
            if (freeRoomsList.contains(tmpRoom)) {
                resultRooms.add(tmpRoom);
            }
        }

        this.setListRooms(resultRooms);

        return new ForwardResolution("/frontend/roomsList.jsp").addParameter("duration", duration).addParameter("startDate", day + ". " + month + ". " + year).addParameter("startDateRaw", dateToFormat);
    }

    @ValidationMethod(on = "quickSearch")
    public void validateQuickSearch(ValidationErrors errors) {
        if (getContext().getRequest().getParameter("quick_search_city").isEmpty()) {
            errors.add("quick_search_city", new LocalizableError("frontend.valueRequired"));
        }

        if (getContext().getRequest().getParameter("quick_search_maxPrice").isEmpty()) {
            errors.add("quick_search_maxPrice", new LocalizableError("frontend.valueRequired"));
        }

        if (getContext().getRequest().getParameter("quick_search_minCapacity").isEmpty()) {
            errors.add("quick_search_minCapacity", new LocalizableError("frontend.valueRequired"));
        }
    }

    @ValidationMethod(on = "advancedSearching")
    public void validateAdvancedSearch(ValidationErrors errors) {
        try {
            Integer.valueOf(getContext().getRequest().getParameter("duration"));
        } catch (NumberFormatException ex) {
            errors.add("duration", new LocalizableError("frontend.mustBePositiveNumber"));
        }

        try {
            Integer.valueOf(getContext().getRequest().getParameter("minCapacity"));
        } catch (NumberFormatException ex) {
            errors.add("minCapacity", new LocalizableError("frontend.mustBePositiveNumber"));
        }

        try {
            BigDecimal.valueOf(Integer.valueOf(getContext().getRequest().getParameter("maxPrice")));
        } catch (NumberFormatException ex) {
            errors.add("maxPrice", new LocalizableError("frontend.mustBePositiveNumber"));
        }

        listHotel = hotelAdapter.findAllHotels();
    }

    @ValidationMethod(on = "hotelFreeRooms")
    public void validateRoomsList(ValidationErrors errors) {
        try {
            Integer.valueOf(getContext().getRequest().getParameter("duration"));
        } catch (NumberFormatException ex) {
            errors.add("duration", new LocalizableError("frontend.mustBePositiveNumber"));
        }
    }

    public List<HotelDTO> getListHotels() {
        if (this.listHotel == null) {
            return null;
        }
        return Collections.unmodifiableList(listHotel);
    }

    public void setListHotels(List<HotelDTO> list) {
        this.listHotel = new ArrayList<HotelDTO>();
        this.listHotel.addAll(list);
    }

    public List<RoomDTO> getListRooms() {
        if (this.listRoom == null) {
            return null;
        }
        return Collections.unmodifiableList(listRoom);
    }

    public void setListRooms(List<RoomDTO> list) {
        this.listRoom = new ArrayList<RoomDTO>();
        this.listRoom.addAll(list);
    }

    public HotelDTO getHotel() {
        return hotel;
    }

    public void setHotel(HotelDTO hotel) {
        this.hotel = hotel;
    }

    public RoomDTO getRoom() {
        return room;
    }

    public void setRoom(RoomDTO room) {
        this.room = room;
    }

    @ValidateNestedProperties({
        @Validate(on = {"executeBooking"}, field = "firstName", required = true),
        @Validate(on = {"executeBooking"}, field = "surName", required = true),
        @Validate(on = {"executeBooking"}, field = "numberOfIdCard", required = true)
    })
    public List<CustomerDTO> getCustomers() {
        return customers;
    }

    public void setCustomers(List<CustomerDTO> customers) {
        this.customers = new ArrayList<CustomerDTO>();
        this.customers.addAll(customers);
    }
}
